package adv.i31.RobotMin3d;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.ParcelUuid;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

public class BluetoothUi extends BroadcastReceiver
{
	public interface Callback
	{
		public void onConnect(BluetoothConnection con);

		public void onDisconnect();
	}

	private ArrayList<Callback>	mCallbacks	= new ArrayList<Callback>();

	public void addCallback(Callback c)
	{
		mCallbacks.add(c);
	}

	public void removeCallback(Callback c)
	{
		mCallbacks.remove(c);
	}

	private static final String			TAG				= "Bluetoothtest";
	private static UUID					uuid			= UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");

	private ArrayAdapter<RobotDevice>	mDevicesAdapter;
	private ArrayList<RobotDevice>		mDevices;

	private ListView					mListView;
	private Button						mScanButton;
	private Button						mStartServerButton;

	private MainActivity				parentActivity;

	private Thread						mServerThread	= null;
	private BluetoothAdapter			mAdapter;

	private RobotDevice					mConnectTarget;
	private Thread						mClientThread	= null;
	private BluetoothSocket				mSocket;
	private BluetoothSocket				mSocket2;

	private void quickToast(String s)
	{
		Toast.makeText(parentActivity, s, Toast.LENGTH_SHORT).show();
	}

	public void init(MainActivity parent)
	{
		parentActivity = parent;

		// get access to the ui views
		mListView = (ListView) parent.findViewById(R.id.lstBtDevices);
		mScanButton = (Button) parent.findViewById(R.id.btnStartScan);
		mStartServerButton = (Button) parent.findViewById(R.id.btnStartServer);

		// Initialize BroadcastReceivers to handle BT device search
		IntentFilter filter = new IntentFilter();
		filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
		filter.addAction(BluetoothDevice.ACTION_FOUND);
		filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
		filter.addAction(BluetoothDevice.ACTION_UUID);
		filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
		parent.registerReceiver(this, filter);

		// bluetooth stuff
		// make sure the device supports Bluetooth
		mAdapter = BluetoothAdapter.getDefaultAdapter();
		if (mAdapter == null) {
			throw new RuntimeException("Device does not support Bluetooth");
		}

		// make sure the Bluetooth adapter is active
		if (!mAdapter.isEnabled()) {
			Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			parentActivity.startActivity(enableBtIntent);
		}

		// All the discovered BT devices will go here
		mDevices = new ArrayList<RobotDevice>();

		// the Adapter will handle the population of the ListView with items
		// from the ArrayList
		mDevicesAdapter = new ArrayAdapter<RobotDevice>(parentActivity, R.layout.listitem, R.id.listitem, mDevices);
		mListView.setAdapter(mDevicesAdapter);

		// add all bonded devices to the list
		Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
		for (BluetoothDevice d : bondedDevices) {
			mDevices.add(new RobotDevice(d));
		}

		mStartServerButton.setOnClickListener(new OnClickListener()
		{
			public void onClick(View v)
			{
				parentActivity.runOnUiThread(new Runnable()
				{
					@Override
					public void run()
					{
						mStartServerButton.setText("server running...");
					}
				});
				listenAsync();
			}
		});

		mScanButton.setOnClickListener(new OnClickListener()
		{
			public void onClick(View v)
			{
				/*
				 * Intent discoverableIntent = new
				 * Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
				 * discoverableIntent
				 * .putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
				 * parentActivity.startActivity(discoverableIntent);
				 */

				if (mAdapter.isDiscovering()) {
					mAdapter.cancelDiscovery();
				} else {
					// Start scanning for BT devices
					mAdapter.startDiscovery();
				}
			}
		});

		mListView.setOnItemClickListener(new OnItemClickListener()
		{
			public void onItemClick(AdapterView<?> parent, View view, int position, long id)
			{
				connectAsync(mDevices.get(position));
			}
		});

	}

	private void disconnect()
	{
		if (mSocket != null) {
			try {
				mSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			for (Callback c : mCallbacks)
				c.onDisconnect();
			mSocket = null;
		}
	}

	private void connect(RobotDevice rd)
	{
		BluetoothDevice device = rd.mDevice;

		Log.i(TAG, "connecting to: " + rd + " " + device);

		// if we aren't currently connecting/connected to a device
		if (mSocket == null) {
			try {
				Log.i(TAG, "creating socket");
				// mSocket = device.createRfcommSocketToServiceRecord(uuid);

				mSocket = device.createInsecureRfcommSocketToServiceRecord(uuid);

				Log.i(TAG, "connecting");
				mSocket.connect();

				for (Callback c : mCallbacks)
					c.onConnect(new BluetoothConnection(mSocket));
				

				new Thread(new Runnable()
				{

					@Override
					public void run()
					{
						try {
							BufferedReader br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
							while (true) {
								Log.i(TAG,"read line" +  br.readLine());
							}
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}
				}).run();

			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void connectAsync(RobotDevice target)
	{
		if (mClientThread == null) {
			mConnectTarget = target;
			mClientThread = new Thread(new Runnable()
			{
				@Override
				public void run()
				{
					connect(mConnectTarget);
					mClientThread = null;
				}
			});
			mClientThread.start();
		}
	}

	private void listen()
	{
		mSocket2 = null;
		BluetoothServerSocket serverSocket = null;
		try {
			serverSocket = mAdapter.listenUsingRfcommWithServiceRecord(TAG, uuid);

			// serverSocket =
			// mAdapter.listenUsingInsecureRfcommWithServiceRecord(TAG, uuid);

			mSocket2 = serverSocket.accept();
			Log.i(TAG, "client connected: " + mSocket2.toString());


			for (Callback c : mCallbacks)
				c.onConnect(new BluetoothConnection(mSocket2));

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (serverSocket != null) {
					serverSocket.close();
				}

				// if (mSocket != null) {mSocket.close(); }
			} catch (IOException e) {
				e.printStackTrace();
			}
			// mSocket = null;
		}
		parentActivity.runOnUiThread(new Runnable()
		{
			@Override
			public void run()
			{
				mStartServerButton.setText("start server");
			}
		});
	}

	void listenAsync()
	{
		// start the server thread if it isn't already running
		if (mServerThread == null) {
			mServerThread = new Thread(new Runnable()
			{
				@Override
				public void run()
				{
					listen();
					mServerThread = null;
				}
			});
			mServerThread.start();
		}
	}

	// called for events regarding bluetooth device discovery
	// populates the device list
	@Override
	public void onReceive(Context context, Intent intent)
	{
		String action = intent.getAction();

		Log.i(TAG, "received action: " + action);

		if (BluetoothDevice.ACTION_FOUND.equals(action)) {
			// get the discovered remote BT device
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			Log.i(TAG, "found device: " + device.toString());
			// fetch the available services asynchronously
			if (!device.fetchUuidsWithSdp())
				Log.i(TAG, "fetchUUids returned false");

			RobotDevice rd = new RobotDevice(device);
			if (!mDevices.contains(rd)) {
				// if the device is not already in the list

				mDevices.add(rd);
				// notify the adapter that the ListView needs to be
				// redrawn because an item was added
				mDevicesAdapter.notifyDataSetChanged();
			} else {
				quickToast("Device: " + rd.toString() + " already listed");
			}

			// Stop the search after 1 device was found
			mAdapter.cancelDiscovery();

		} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
			mScanButton.setText("Start Scan");

		} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
			mScanButton.setText("Scanning... (cancel scan)");

		} else if (BluetoothDevice.ACTION_UUID.equals(action)) {
			/*
			 * BluetoothDevice device =
			 * intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			 * Parcelable[] puuid =
			 * intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
			 * 
			 * Log.i(TAG, "intent: " + intent.toString());
			 * 
			 * if (puuid != null) { for (Parcelable p : puuid) Log.i(TAG,
			 * p.toString()); } else { Log.i(TAG, "parcelable uuid was null"); }
			 * 
			 * Log.i(TAG, device + " " + puuid);
			 */

		} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
			// when a connection is terminated
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			for (Callback c : mCallbacks)
				c.onDisconnect();
			mSocket = null;
		}
	}

}
